﻿//#define _CRT_SECURE_NO_WARNINGS
//#include <stdio.h>
//#include <string.h>
//#include <errno.h>
//#include <stdlib.h>
//#include "字符串函数标头.h"
//#include "内存函数标头.h"
//#include "部分剩余作业标头.h"
//#include "结构体标头.h"

#include "common.h"

/*
字符串、字符函数：
	1.模拟实现字符串长度函数 strlen()
	2.模拟实现字符串复制函数 strcpy()
		strcpy 函数的使用需要注意以下几点：
			a.目标字符串的长度不能小于源字符串的长度，否则会导致缓冲区溢出。
			b.源字符串的末尾必须要有空字符，否则会导致越界。
			c.源字符串中 '\0' 提前出现会导致字符串的截断。
			d.目标字符串是可以修改的，因此需要注意字符串的初始化问题。
	3.模拟实现字符串拼接函数 strcat()
		strcat 函数用于字符串追加，注意事项和 strcpy 函数相同。
	4.模拟实现字符串比较函数 strcmp()
	5.模拟实现字符串查找子串函数 strstr()，函 KMP 算法的实现
	6.strtok 函数的学习
	7.strerror 函数的学习
	8.字符分类函数（头文件为 ctype.h）返回非零表示是指定字符，0 表示不是指定字符。：
		int isalnum(const int c) 判断字符是否是字母或数字（大小写字母或数组）
		isalpha() 判断字符是否是字母
		isdigit() 判断字符是否是数字
		isspace() 判断字符是否是空白字符，包括\t、\n、\v、\f、\r和空格。
		isxdigit() 判断字符是否是十六进制数字（大小写的 a-f 和 0-9）
		isdigit() 判断字符是否是十进制数字
		isupper() 判断字符是否是大写字母
		islower() 判断字符是否是小写字母
		iscntrl() 判断字符是否是控制字符
		ispunct() 判断字符是否是标点符号
		isgraph() 判断字符是否是可见字符（不包括空格、制表符、换行符等）任何图像字符
		isprint() 判断字符是否是可打印字符（不包括空格、制表符、换行符等）
	9.字符串或字符转换函数（头文件为 stdlib.h）：
		int atoi(const char* str) 字符串转换为整数
		double atof(const char* str) 字符串转换为浮点数
		char* _itoa(int value, char *string, int radix)
			整数转换为字符串，radix 为进制（比如 10 表示将 value 视为十进制）
			char* string 地址存放转换后的字符串
		int toupper(int c); 字符c转换为大写字母，非字母则返回原值
		tolower() 转换为小写字母
	10.字符串处理函数：
		char *_strrev( char *string ); 字符串反转
		strtok() 字符串分割
		char *strchr( const char *string, int c ); 字符串查找单个字符
		strrchr() 字符串查找单个字符的反向查找
		size_t strspn( const char *string, const char *strCharSet ); 字符串前缀长度
		strcspn() 字符串后缀长度
		char* strpbrk( const char *string, const char *strCharSet ); 字符串查找字符
			在 string   中查找 strCharSet 中的任一字符，返回该字符的地址，
			如果没有找到，则返回 NULL。
			每查找一次，下次查找会从上次找到字符的下一个位置开始查找。
		strstr() 字符串查找子串
		strerror() 错误信息字符串

内存函数：
	1.void *memcpy( void *dest, const void *src, size_t count ); 函数的学习
		memcpy 用于根据传入的 count 值，从源地址 src 复制 count 个字节到目的地址 dest。
		注意：
			a.源地址和目的地址最好不能重叠。
			b.源地址和目的地址最好为相同的数据类型。
		在 vs 里面，memcpy 可以做到和 memmove 函数一样的功能（拷贝重叠地址）
	2.int memcmp( const void *buf1, const void *buf2, size_t count ); 函数的学习
	3.void *memset( void *dest, int c, size_t count ); 函数的学习

*/

int main()
{
	//system("chcp 65001 > nul"); // 设置终端输出编码为 UTF-8
	//SetConsoleOutputCP(65001); // 同上
	// 字符串函数部分
	{
		print_start_line("字符串函数部分开始");
		char str1[] = "Hello, world!";
		// 模拟字符串长度的实现
		printf("The length of the string is: %zd\n", my_strlen1(str1));

		char str2[100] = { 0 };
		char str2_copy[100] = { 0 };
		// 模拟字符串复制的实现
		my_strcpy(str2, str1);
		printf("The copied string is: %s\n", str2);
		my_strcpy_n(str2_copy, str1, 5);
		printf("The copied five string is: %s\n", str2_copy);
		printf("模拟字符串复制的实现结束\n\n");

		char str3[] = " Goodbye, world!";
		// 模拟字符串拼接的实现
		my_strcat(str2, str3);
		printf("The concatenated string is: %s\n", str2);
		my_strcat_n(str2_copy, str3, 5);
		printf("The concatenated five string is: %s\n", str2_copy);
		printf("模拟字符串拼接的实现结束\n\n");

		// 模拟字符串比较的实现
		char str4[] = "Hello, world!";
		str_compare(str1, str4);
		str_compare(str2, str3);
		str_compare(str1, str2);
		str_compare("我是傻逼", "傻逼是我"); // 注意：中文字符的比较需要注意编码问题。
		str_compare_n(str1, str4, 5); // 注意：中文字符的比较需要注意编码问题。
		str_compare_n(str2, str3, 5); // 注意：中文字符的比较需要注意编码问题。
		str_compare_n(str1, str2, 5); // 注意：中文字符的比较需要注意编码问题。
		printf("模拟字符串比较的实现结束\n\n");

		// 模拟 strstr 函数的实现
		char test_str1[] = "吾名为486，aaa幼幼幼女精灵使者，"
			"吾有不可告人的秘密，就是死亡回归";
		printf("search result: %s\n", my_strstr1(test_str1, "aa幼"));
		printf("search result: %s\n", my_strstr1(test_str1, "找不到我"));
		printf("search result: %s\n", my_strstr2(test_str1, "幼幼女"));
		printf("search result: %s\n", my_strstr2(test_str1, "a幼幼女"));
		// 模拟 KMP 算法的版本
		char txt[] = "ABDABDABDABCDBC";
		char pat[] = "ABDABCD";
		printf("KMP search result: %s\n", KMPsearch(txt, pat));

		// strtok 函数的学习
		char str5[] = "Hello，world！你好，世纪！我来了。"; // “界”会出现乱码，不知道为什么
		char cpy[100];
		strcpy(cpy, str5);
		char* token = "！，。"; // 注意是中文符号
		char* p_null = NULL;
		for (p_null = strtok(cpy, token); p_null; p_null = strtok(NULL, token))
			printf("%s\n", p_null);

		// strerror 函数的学习
		FILE* pf = fopen("test.txt", "r"); // 打开文件，如果文件不存在，则返回 NULL
		if (pf == NULL)
			printf("Error: %s\n", strerror(errno));
		/*在C语言中，errno是一个全局变量，它用来存放系统调用的错误代码。
		 通过errno，我们可以获得系统调用失败的原因。
		 errno的值是系统定义的，不同系统可能有不同的定义。errno的定义在errno.h中。*/
		 //fclose(pf); // 关闭文件

		 //printf("%s\n", _itoa(123456, str2, 10)); // 整数转换为字符串
		 //printf("str2: %s\n", str2); // 输出结果：123456，说明 str2 已经被修改了。

		print_end_line("字符串函数部分结束");
	}

	// 内存函数部分
	{
		print_start_line("内存函数部分开始");
		// memcpy 函数的学习，并模拟实现
		int a1[5] = { 1, 2, 3, 4, 5 };
		int b1[5] = { 0 };
		my_memcpy(b1, a1, sizeof(a1));

		int a2[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
		my_memmove(a2, a2 + 2, 3 * sizeof(int));
		my_memmove(a2 + 7, a2 + 5, 3 * sizeof(int));
		printf("The copied array a2 is: \n");
		for (int i = 0; i < 10; i++)
			printf("%d ", a2[i]);
		printf("\n");

		// memcmp 函数的学习
		int a3[5] = { 1, 2, 3, 4, 5 };
		int b3[5] = { 1, 2, 3, 4, 5 };
		int c3[5] = { 1, 2, 1, 4, 6 };
		printf("The result of memcmp a3 and b3 is: %d\n",
			memcmp(a3, b3, sizeof(a3)));
		printf("The result of memcmp a3 and c3 is: %d\n",
			memcmp(a3, c3, sizeof(a3)));//似乎只输出 -1、0、1

		// memset 函数的学习
		char a4[11] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j' };
		memset(a4 + 2, 'x', 5); // 注意传入指针地址的值是可以修改的
		/*int a5[5] = {0};
		memset(a5, 1, sizeof(a5)); // 每个 int 对应值为 0x 01 01 01 01*/
		printf("The filled array a4 is: %s\n", a4);

		print_end_line("内存函数部分结束");
	}

	// 部分剩余作业
	{
		print_start_line("部分剩余作业开始");
		// 改变数字每一位为 1 或 0
		int num1 = -213456788;
		change_num(&num1);
		printf("The changed number is: %d\n", num1);

		// 打印带 * 的等腰直角三角形
		print_triangle(5);

		// 比赛名次
		rank_players();

		// 找凶手
		search_killer();

		// 打印杨辉三角
		print_YangHui_triangle(14);

		// 左旋字符串中 k 个字符
		char str6[] = "abcdefghijklmnopqrstuvwxyz";
		left_rotate(str6, 3);
		printf("The rotated string is: %s\n", str6);

		// 杨氏矩阵查找值，要求时间复杂度小于 O(n)
		int matrix[][5] = { {1, 2, 3, 4, 5},
							{6, 7, 8, 9, 10},
							{11, 12, 13, 14, 15},
							{16, 17, 18, 19, 20} }; // 杨氏矩阵
		int rows = sizeof(matrix) / sizeof(matrix[0]); // 行数
		int cols = sizeof(matrix[0]) / sizeof(matrix[0][0]); // 列数
		int search = 17;
		struct coord cod1 = search_num_arr(matrix, rows, cols, search);
		printf("The returned coord is: (%d, %d)\n", cod1.x, cod1.y);

		// 判断字符串是否由另一个字符串左旋或右旋得到
		char str7[] = "abcdefg";
		char str8[] = "efgabcd";
		char str9[] = "gabcdef";
		//printf("%s\n", right_rotate(str8, 4));
		int rot_num1 = is_rotate_right(str7, strlen(str7), str8, strlen(str8));
		if (rot_num1 >= 0)
			printf("The string is rotated %d times to the right\n", rot_num1);
		else
			printf("The string is not rotated string\n");
		int rot_num2 = is_rotate_left(str7, strlen(str7), str9, strlen(str9));
		if (rot_num2 >= 0)
			printf("The string is rotated %d times to the left\n", rot_num2);
		else
			printf("The string is not rotated string\n");

		// 矩阵转置
		int matrix2[3][4] = { {1, 2, 3, 4},
							  {5, 6, 7, 8},
							  {9, 10, 11, 12} }; // 矩阵
		int* p_arr[3] = { matrix2[0], matrix2[1], matrix2[2] }; // 指针数组
		int (*p_mat)[3] = &p_arr; // 数组指针，指向指针数组，指针数组中有 3 个指针

		int rows2 = sizeof(matrix2) / sizeof(matrix2[0]); // 行数
		int cols2 = sizeof(matrix2[0]) / sizeof(matrix2[0][0]); // 列数
		int** p_tra = transpose_matrix(p_mat, rows2, cols2);
		printf("The transposed matrix is: \n");
		for (int i = 0; i < cols2; i++)
		{
			for (int j = 0; j < rows2; j++)
				printf("%d ", p_tra[i][j]);
			printf("\n");
		}

		// 上三角矩阵的判定
		int matrix3[4][4] = { {1, 2, 3, 4},
							  {0, 3, 4, 6},
							  {0, 0, 5, 6},
							  {0, 0, 0, 7} }; // 上三角矩阵
		int* p_arr3[4] = { // 指针数组
			matrix3[0], matrix3[1], matrix3[2], matrix3[3] };
		int (*p_mat3)[4] = &p_arr3; // 数组指针，指向指针数组，指针数组中有 4 个指针
		if (is_upper_triangle(p_mat3, 4, 4)) // 输出 1
			printf("True\n");
		else
			printf("False\n");

		// 有序数组判断
		int arr_sort1[5] = { 1, 1, 3, 4, 5 };
		int arr_sort2[5] = { 10, 9, 7, 7, 6 };
		int arr_sort3[5] = { 1, 5, 9, 3, 7 };
		int arr_sort4[5] = { 1, 1, 1, 1, 1 };
		printf("arr_sort1 %s\n", is_sorted(arr_sort1, 5));
		printf("arr_sort2 %s\n", is_sorted(arr_sort2, 5));
		printf("arr_sort3 %s\n", is_sorted(arr_sort3, 5));
		printf("arr_sort3[0] %s\n", is_sorted(arr_sort3, 1));
		printf("arr_sort4 %s\n", is_sorted(arr_sort4, 5));

		print_end_line("部分剩余作业结束");
	}
	return 0;
}